/*************************************************************************
 * All portions of code are copyright by their respective author/s.
 * Copyright (C) 2009      Bryan Christ <bryan.christ@hp.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------*/

#include <stdlib.h>
#include <string.h>

#include "macros.h"
#include "ps_task.h"

ps_task_t*
ps_task_create(PSTHREAD callback,void *anything)
{
	ps_task_t	*task;

	if(callback == NULL) return NULL;

	task = (ps_task_t*)calloc(1,sizeof(ps_task_t));
	task->callback = callback;
	task->anything = anything;

	return task;
}

ps_task_t*
ps_task_fetch(ps_task_t *task_list,int16_t offset)
{
    ps_task_t   *node;
	int16_t     i;
	int16_t	    delta;


	if(task_list == NULL) return NULL;

    node = task_list;

    if(offset == 0) return NULL;

	delta = ABSINT(offset);
	for(i = 0;i < delta;i++)
	{
		if(offset < 0)
		{
			if(node->prev == NULL) break;
			node = node->prev;
			continue;
		}

		if(node->next == NULL) break;
		node = node->next;
	}

	return node;
}

void
ps_task_swap(ps_task_t *node1,ps_task_t *node2)
{
	if(node1 == NULL || node2 == NULL) return;

    if(node1 == node2) return;

	node1->next = node2->next;
	node2->prev = node1->prev;
	node1->prev = node2;
	node2->next = node1;

	if(node1->next != NULL) node1->next->prev = node1;
	if(node2->prev != NULL) node2->prev->next = node2;

	return;
}

void
ps_task_append(ps_task_t *task_list,ps_task_t *node)
{
    ps_task_t   *old_tail;

	if(node == NULL || task_list == NULL) return;
    if(task_list == node) return;

    old_tail = task_list;

    while(old_tail->next != NULL)
    {
        old_tail = old_tail->next;
    }

    node->head = task_list->head;
    node->next = NULL;

    old_tail->next = node;
    node->prev = old_tail;

	return;
}

void
ps_task_prepend(ps_task_t *task_list,ps_task_t *node)
{
    ps_task_t   *old_head;

	if(node == NULL || task_list == NULL) return;
    if(task_list == node) return;

    old_head = task_list;

    node->prev = NULL;

    node->next = old_head;
    old_head->prev = node;

	return;
}

/*
void
ps_task_move(ps_task_t *task,int16_t offset)
{
	ps_task_t	*copy;

	if(offset == 0) return;

	copy = calloc(1,sizeof(ps_task_t));
	memcpy(copy,task,sizeof(ps_task_t));

	if(offset < 1) ps_task_append(copy,task,offset);
	else ps_task_prepend(copy,task,offset);

	ps_task_unlink(task);
	ps_task_destroy(task);

	return;
}
*/

inline void
ps_task_shift_down(ps_task_t *task)
{
    if(task == NULL) return;

    // can't shift any further
    if(task->next == NULL) return;

    ps_task_swap(task,task->next);

    return;
}

inline void
ps_task_shift_up(ps_task_t *task)
{
    if(task == NULL) return;

    // can't shift any further
    if(task->prev == NULL) return;

    ps_task_swap(task,task->prev);

    return;
}

ps_task_t*
ps_task_unlink(ps_task_t *task)
{
    ps_task_t   *node;

	if(task == NULL) return NULL;

    // if this node was the only one
    if(task->next == NULL && task->prev == NULL)
    {
        return NULL;
    }

    // if this node was the tail...
    if(task->next == NULL)
    {
        if(task->prev != NULL) task->prev->next = NULL;
        node = task->head;
        return node;
    }

    // if this node was the head...
    if(task->prev == NULL)
    {
        if(task->next != NULL)
        {
            node = task->next;
            node->prev = NULL;
        }

        task->next = NULL;
        return node;
    }

    // if this task was neither the head or the tail


	if(task->prev != NULL) task->prev->next = task->next;
	if(task->next != NULL) task->next->prev = task->prev;

	return task->head;
}

ps_task_t*
ps_task_destroy(ps_task_t *task)
{
    ps_task_t   *new_head;

	if(task == NULL) return NULL;

    new_head = ps_task_unlink(task);

	free(task);

	return new_head;
}
